﻿using System.Collections.Generic;
using FunctionalCLR.Core;
using System;

namespace FunctionalCLR.Shell {
    public class Context : Dictionary<string, object>, IContext {

        public bool Evaluated {
            get;
            set;
        }

        private IFunctionManager funMgr;

        private ContextParserFactory cpf = null;

        public List<string> InputHistory = new List<string>();

        public List<object> OutputHistory = new List<object>();

        public List<Exception> ErrorHistory = new List<Exception>();

        public Context() {
            funMgr = new ContextFunctionManager(this);
            cpf = new ContextParserFactory(this);
        }

        public virtual object AddVariable(object factorName) {
            Variable rf = new Variable();
            rf.Name = factorName.ToString();

            this[rf.Name] = rf;
            return rf;
        }

        public virtual Node Parse(string expr) {
            Parser p = this.cpf.CreateParser(expr, this);
            Node root = p.Parse();
            return root;
        }

        public virtual object Evaluate(string expression) {
            Node root = Parse(expression);
            object result = root.Evaluate();

            return result;
        }

        public virtual object GetVariableByName(object token) {
            if (!this.ContainsKey(token.ToString())) {
                return null;
            }

            return this[token.ToString()];
        }

        public virtual object Assign(object obj, object result) {
            if (obj is string) {
                Variable var = null;
                if (this.ContainsKey(obj.ToString())) {
                    var = (Variable)this[obj.ToString()];
                } else {
                    var = new Variable();
                    this[obj.ToString()] = var;
                }

                var.Value = result;
            } else if (obj is Variable) {
                ((Variable)obj).Value = result;
            }

            return result;
        }

        public virtual object AddVariableWithTypeInference(
            string p, object obj) {
            return this.AddVariable(p);
        }

        public object[] GetAllVariables() {
            throw new NotImplementedException();
        }

        public void RemoveVariable(string p) {
            Remove(p);
        }

        public bool HasVariable(string p) {
            return this.ContainsKey(p);
        }

        #region IContext Members

        public IParserFactory GetParserFactory() {
            return this.cpf;
        }

        #endregion

        #region IContext Members


        public IFunctionManager GetFunctionManager() {
            return funMgr;
        }

        #endregion
    }
}
